<template>
  <div ref="scrollWrap" class="virtual-scroll-wrap">
    <FragmentItem
        v-for="(item, index) in list"
        :key="item.guid"
        v-model="activeId"
        :clip-index="index"
        :data.sync="item"
        :clip-data="clipData"
    />
    <div class="fixed-info">
      <div class="title">Count: {{ list.length }}</div>
      {{ clipData }}
    </div>
  </div>
</template>

<script>
import FragmentItem from "./FragmentItem"
import axios from "axios"

const enableVirtualScroll = true
const testDataRepeat = 1

// 防抖函数
const debounce = (fn, ms) => {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn(...args)
    }, ms)
  }
}

export default {
  name: "VirtualScroll",
  components: {
    FragmentItem
  },
  data() {
    return {
      activeId: 0,
      list: [],
      clipData: {},
      lenes: []
    }
  },
  async created() {
    this.handleResize = debounce(() => {
      this.initScrollWrap()
      this.countData = this.getAvailableItemCount()
      this.toggleRenderItems()
    }, 300)
  },
  async mounted() {
    const {data} = await axios.get('/data.json')
    // console.log(data)
    let list = data.list.map(item => ({...item, isRender: !enableVirtualScroll}))
    const newList = []

    let counter = 0
    // 暴力测试，增加循环次数
    for (let i = 0; i < testDataRepeat; i++) {
      list.forEach(item => {
        newList.push({
          ...item,
          guid: counter
        })
        counter++
      })
    }

    console.log(`共有 ${newList.length} 项`)

    this.list = newList
    this.clipData = data.clipData
    this.lenes = data.lenes

    if (enableVirtualScroll) {
      this.initScroll()
    }

  },
  beforeDestroy() {
    window.removeEventListener('resize', this.handleResize)
  },
  methods: {
    getAvailableItemCount() {
      const wrapOption = this.wrapOption
      const itemOption = this.itemOption
      const wCount = Math.max(1, Math.floor(wrapOption.width / itemOption.width))
      const hCount = Math.ceil(wrapOption.height / itemOption.height)
      const count = wCount * hCount
      console.log(`横向可容纳 ${wCount} 格，纵向可容纳 ${hCount} 格，需要渲染 ${count} 项`)
      return {wCount, hCount, count}
    },
    initScrollWrap() {
      const scrollWrap = this.$refs.scrollWrap
      console.log(scrollWrap)

      const rect = scrollWrap.getBoundingClientRect()
      this.wrapOption = {
        width: rect.width,
        height: rect.height
      }

      return scrollWrap
    },
    toggleRenderItems() {
      const scrollWrap = this.$refs.scrollWrap
      const scrollTop = scrollWrap.scrollTop
      console.log({scrollTop})

      const lines = Math.ceil(scrollTop / this.itemOption.height)
      let skip = lines * this.countData.wCount

      const len = this.list.length
      let {count, wCount} = this.countData
      count += skip

      console.log(`上下额外加载 ${wCount} 项`)

      const fromIndex = Math.min(len, Math.max(0, skip - wCount))
      const toIndex = Math.min(len, count + wCount)

      if (this.lastRenderedIndex) {
        let {fromIndex: fIndex, toIndex: tIndex} = this.lastRenderedIndex
        console.log({fIndex, tIndex})
        if (fIndex < fromIndex) {
          tIndex = Math.min(tIndex, fromIndex)
          console.log(`[正向] 卸载 ${fIndex} ~ ${tIndex} 项`)
          for (let i = fIndex; i < tIndex; i++) {
            this.list[i].isRender = false
          }
        } else {
          fIndex = Math.max(fIndex, toIndex)
          console.log(`[负向] 卸载 ${fIndex} ~ ${tIndex} 项`)
          for (let i = fIndex; i < tIndex; i++) {
            this.list[i].isRender = false
          }
        }


      }
      console.log(`渲染 ${fromIndex} ~ ${toIndex - 1} 项`)
      for (let i = fromIndex; i < toIndex; i++) {
        this.list[i].isRender = true
      }
      // 全量卸载
      // if (fromIndex > 0) {
      //   console.log(`卸载 ${0} ~ ${fromIndex} 项`)
      //   for (let i = 0; i < fromIndex; i++) {
      //     this.list[i].isRender = false
      //   }
      // }
      // if (toIndex < len) {
      //   console.log(`卸载 ${toIndex} ~ ${len} 项`)
      //   for (let i = toIndex; i < len; i++) {
      //     this.list[i].isRender = false
      //   }
      // }

      this.lastRenderedIndex = {
        fromIndex,
        toIndex
      }
    },
    initScroll() {
      const scrollWrap = this.initScrollWrap()
      this.itemOption = {
        width: 230 - 1,
        height: 92 - 1
      }
      window.addEventListener('resize', this.handleResize)

      this.countData = this.getAvailableItemCount()

      this.toggleRenderItems()

      const handleScroll = debounce(() => {
        this.toggleRenderItems()
      }, 50)
      scrollWrap.onscroll = (e) => {
        handleScroll(e)
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.virtual-scroll-wrap {
  background: #111111;
  width: 100%;
  height: 100vh;

  flex: 1;
  overflow-y: auto;
  overflow-y: overlay; // 滚动条覆盖
  padding: 0;


  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: flex-start;
}

.fixed-info {
  position: fixed;
  top: 100px;
  left: 0;
  width: 200px;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid red;
  font-size: 12px;
  color: white;
  padding: 10px;

  .title {
    font-size: 23px;
    font-weight: bold;
  }
}
</style>
